//
//  JPSwiftDictModel.swift
//  JP-字典转模型
//
//  Created by soulPo on 15/3/4.
//  Copyright (c) 2015年 soulPo. All rights reserved.
//

import Foundation

/**
    模型中的基本数据类型 一定不能写成可选项
    应该写成 var num:Int
*/

///  字典转模型协议
// 在Swift中让类动态调用 协议方法， 需要使用 @objc 的关键字
@objc protocol DictModelProtocol {
    ///  自定义映射表
    ///
    ///  :returns: 返回可选映射关系字典
    static func customeClassMapping() -> [String:String]?
}


///  字典转模型工具
class JPSwiftDictModel {
    /// 单例 全局访问入口
    public static let sharedManager = JPSwiftDictModel()
    private init() {}
    ///  将字典转成模型对象
    ///
    ///  :param: dict 字典
    ///  :param: cls  模型类
    ///
    ///  :returns: 实例化的模型对象
    public func objectWithDictionary(dict: NSDictionary, modelClass: AnyClass) ->AnyObject? {
        // 1 取出模型类的字典
        let dictInfo = fullModelInfo(modelClass)
        // 实例化对象
        let obj: AnyObject = modelClass.alloc()
        
        // 2 遍历模型字典,有什么属性设置什么属性
        // k 应该和 dict 中的key 一致
        for (k, v) in dictInfo {
            // 取出字典中的内容
            if let value: AnyObject? = dict[k] {
//                println("要设置数 值的 \(value) + key \(k)")
                // 判断是否是自定义对象
                if v.isEmpty && !(value === NSNull()){
                    if k == "d" {
                        println(value)
                    }
                    obj.setValue(value, forKey: k)
                } else {
                    // 取出数值的类型 classForCoder
                    let type = "\(value!.classForCoder)"
//                    println("\t自定义对象\(value)+ key=\(k)---\(type)")
                    // 可能两种结果 字典或数组
                    
                    if type == "NSDictionary" {
                        // value 是字典转模型
                        println("!!!!")
                        if let subObj: AnyObject? = objectWithDictionary(value as! NSDictionary, modelClass: NSClassFromString(v)) {
                            obj.setValue(subObj, forKey: k)
                            println(subObj)
                        }
                    }
                    if type == "NSArray" {
                        // value 是数组 
                        // 如果是数组如何处理 遍历数组继续处理数组中的字典
                        if let subObj: AnyObject? = objectWithArray(value as! NSArray, cls: NSClassFromString(v)) {
                            obj.setValue(subObj, forKey: k)
                        }
                    }
                }
            }
        }
        
//        println(dictInfo)
        return obj
    }
    
    ///  将数组转为模型对象
    ///
    ///  :param: array 数组 
    ///  :param: cls 模型类
    ///
    ///  :returns: 模型对象
    func objectWithArray(array: NSArray,cls: AnyClass) ->AnyObject? {
        
        // 创建数组
        var result = [AnyObject]()
        // 遍历数组
        for value in array {
            println("111\(value)")
            // 取出数值的类型 classForCoder
            let type = "\(value.classForCoder)"
            
            if type == "NSDictionary" {
                // value 是字典转模型
                if let subObj: AnyObject = objectWithDictionary(value as! NSDictionary, modelClass: cls) {
                    result.append(subObj)
                }
            }
            if type == "NSArray" {
                // value 是数组
                // 如果是数组如何处理 遍历数组继续处理数组中的字典
                if let subObj: AnyObject = objectWithArray(value as! NSArray, cls: cls) {
                    result.append(subObj)
                }
            }
        }
        
        
        return result
    }
    
    
    ///  获取指定类的所有信息
    ///
    ///  :param: modelClass 指定类
    func fullModelInfo(modelClass: AnyClass) -> [String: String] {
        // 判断当前字典是不是已经被缓存
        if let cache = modelCache["\(modelClass)"] {
            println("\(modelClass)已被缓存")
            return cache
        }
        
        // 循环 查找父类
        var currentCls: AnyClass = modelClass
        // 定义一个字典信息
        var dictInfo = [String:String]()
        while let parent: AnyClass = currentCls.superclass(){
            // 当前类的属性信息 添加到 给定的字典信息中
            dictInfo.merge(modelInfo(currentCls))
            
            currentCls = parent
        }
        // 讲模型信息写入缓存
        println("\(modelClass)模型信息写入缓存")
        modelCache["\(modelClass)"] = dictInfo
        return dictInfo
    }
    
    
    ///  获取给定类的信息
    ///
    ///  :param: modelClass 给定类
    func modelInfo(modelClass: AnyClass) -> [String: String]{
//        println(modelClass) // JP_字典转模型.Model
        
        // 判断当前字典是不是已经被缓存
        if let cache = modelCache["\(modelClass)"] {
            println("\(modelClass)已被缓存")
            return cache
        }
        // 判断类是不是遵守了协议, 一旦遵守 说明 有自定义对象
        var mapping: [String: String]?
        if modelClass.respondsToSelector("customeClassMapping") {
            println("实现了协议")
            // 调用协议方法，获取自定义的映射字典
            mapping = modelClass.customeClassMapping()
        }
        
        // 获取类的属性
        var count:UInt32 = 0
        
        // 定义一个属性字典 [属性名称: 自定义对象的名称]
        var dictInfo =  [String : String]()
        
        let ivars = class_copyIvarList(modelClass, &count)
        println("当前模型类有\(count)个属性")
        // 获取每个属性的名称 + 类型
        for i in 0..<count {
            // 索引数组下标只能用Int
            let ivar = ivars[Int(i)]
            
            // UInt8 = char - c语言的
            let cname = ivar_getName(ivar)
            // 讲C 语言的字符串转成 Swift 的String
            let name = String.fromCString(cname)!
            
            // 关于对象的类型 如果是系统的 可以通过kvc赋值
            // 只有自定义对象，才需要做后续的工作
            // 只需要记住自定义对象的类型即可 
            
            // 判断字典中是否存在name
            let type = mapping?[name] ?? ""
            // 设置字典
            dictInfo[name] = type
            
        }
        free(ivars)
        // 讲模型信息写入缓存
        println("\(modelClass)模型信息写入缓存")
        modelCache["\(modelClass)"] = dictInfo
        return dictInfo
    }
    ///  缓存字典 [类名: 模型字典, 类名2: 模型字典]
    var modelCache = [String:[String: String]]()
    
}
///  extension 类似于 OC 的分类 给类添加方法
extension Dictionary {
    ///  给定的字典(可变的)合并到当前字典
    // mutating 标识函数操作的字典是可变的 
    // 泛型 :<K, V> 让函数更富有弹性
    // 任何两个<key, value> 类型 都能转型
    mutating func merge<K,V>(dict:[K: V]) {
         for (k, v) in dict {
            // 分类方法中 要使用updateValue 需要指定类型
            self.updateValue(v as! Value, forKey: k as! Key)
        }
    }
}